

local __pairs__ = pairs;

local function copy(object)
	local lookup_table = {}
    local function _copy(object)
        if type(object) ~= "table" then
            return object
        elseif lookup_table[object] then
            return lookup_table[object]
        end
        local new_table = {}
        lookup_table[object] = new_table
        for index, value in pairs(object) do
            new_table[_copy(index)] = _copy(value)
        end
        return new_table;
    end
    return _copy(object)
end

local function IsScope(scope)
    local metatable = type(scope) == "table" and getmetatable(scope) or nil;
    return metatable and metatable.__is_scope__;
end

local function GetScopeValue(val, NewScope)
    if (type(val) ~= "table" or IsScope(val)) then return val end 
    return NewScope(nil, val);
end

local function TriggerScopeIndexCallBack(scope, key)
    if (type(scope) ~= "table" or key == nil) then return end 
    if (type(scope.__index_callback__) == "function") then return scope.__index_callback__(scope, key) end 

    -- 触发全局回调
    local cur_scope = scope;
    while (cur_scope) do
        -- print("TriggerScopeIndexCallBack", scope, key, cur_scope.__nid__, cur_scope.__index_global_callback__, cur_scope.__parent_scope__);
        if (type(cur_scope.__index_global_callback__) == "function") then cur_scope.__index_global_callback__(scope, key) end 
        cur_scope = cur_scope.__parent_scope__;
    end
end

local function TriggerScopeNewIndexCallBack(scope, key, new_val, old_val)
    if (type(scope) ~= "table" or key == nil) then return end

    if (type(scope.__newindex_callback__) == "function") then return scope.__newindex_callback__(scope, key, new_val, old_val) end 

    -- 触发全局回调
    local cur_scope = scope;
    while (cur_scope) do
        if (type(cur_scope.__newindex_global_callback__) == "function") then cur_scope.__newindex_global_callback__(scope, key, new_val, old_val) end 
        cur_scope = cur_scope.__parent_scope__;
    end
end

local __nid__ = 0;
local function NewScope(env, init_value)
    local __data__ = copy(init_value);
    local __env__ = env;
    local scope_metatable = {};
    local scope = setmetatable({}, scope_metatable);

    if (type(__data__) ~= "table") then __data__ = {} end 
    if (type(__env__) ~= "table") then __env__ = _G end 

    -- 基类Scope
    setmetatable(__data__, {__index = __env__});
    __nid__ = __nid__ + 1;
    scope_metatable.__nid__ = __nid__;
    scope_metatable.__is_scope__ = true;
    scope_metatable.__key__ = nil;
    scope_metatable.__scope__ = scope;
    scope_metatable.__env__ = __env__;
    scope_metatable.__parent_scope__ = IsScope(__env__) and __env__ or nil;
    scope_metatable.__data__ = __data__;
    scope_metatable.__newindex_callback__ = nil;
    scope_metatable.__index_callback__ = nil;
    
    scope_metatable.__newindex_global_callback__ = nil;
    scope_metatable.__index_global_callback__ = nil;

    scope_metatable.__index = function(obj, key)
        if (key == "__is_scope__" or 
            key == "__nid__" or 
            key == "__scope__" or 
            key == "__data__" or 
            key == "__parent_scope__" or 
            key == "__key__" or 
            key == "__index_callback__" or 
            key == "__newindex_callback__" or 
            key == "__index_global_callback__" or 
            key == "__newindex_global_callback__") then return scope_metatable[key] end 
        if (key == "__metatable__") then return scope_metatable end 

        TriggerScopeIndexCallBack(scope, key);

        return __data__[key];
    end

    scope_metatable.__newindex = function(obj, key, val)
        if (key == "__index_callback__" or key == "__newindex_callback__" or key == "__index_global_callback__" or key == "__newindex_global_callback__") then 
            scope_metatable[key] = val;
            return ;
        end 

        local old_val = __data__[key];
        local new_val = GetScopeValue(val, NewScope);
        __data__[key] = new_val;

        if (IsScope(new_val)) then 
            local mt = getmetatable(new_val);
            mt.__key__ = key;
            mt.__parent_scope__ = scope;
        end 

        TriggerScopeNewIndexCallBack(scope, key, new_val, old_val);
    end

    scope_metatable.__len = function(obj)
        return #__data__;
    end

    scope_metatable.__pairs = function(obj)
        return __pairs__(__data__);
    end

    scope_metatable.__call = function(obj)
        return NewScope(obj, scope);
    end

    return scope;
end

return NewScope;